package com.sunyzc.ebaotech.simulator.ybt;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sunyzc.ebaotech.simulator.CodeUtil;
import com.sunyzc.ebaotech.simulator.IOUtil;
import com.sunyzc.ebaotech.simulator.SimulatorProperties;

public abstract class AbstractYBTProcessor implements IYBTProcessor {
	private static final Logger log = LoggerFactory.getLogger(AbstractYBTProcessor.class);
	private String serverHost;
	private YBTClientConfig clientConfig;
	private int dateOffSet = 0;
	private final static int BUFFER_SIZE = 16 * 1024;
	protected Map<String, String> transXmlMap = new HashMap<String, String>();

	@Override
	public String generateRequestBodyTemplateStr(String requestTransType, boolean needRemoveComments) throws Exception {
		String requestBodyContentStr = null;
		InputStream inputStream = null;
		ByteArrayOutputStream outputStream = null;
		try {
			inputStream = this.getClass().getResourceAsStream(requestTransType + ".xml");
			if (inputStream == null)
				throw new NullPointerException("请配置该交易报文的模板！");
			outputStream = new ByteArrayOutputStream();
			byte[] buffer = new byte[BUFFER_SIZE];
			int readLength = 0;
			while ((readLength = inputStream.read(buffer)) != -1)
				outputStream.write(buffer, 0, readLength);
			requestBodyContentStr = new String(outputStream.toByteArray());
			if (needRemoveComments)
				requestBodyContentStr = removeComments(requestBodyContentStr);
		} finally {
			IOUtil.closeQuietly(inputStream);
			IOUtil.closeQuietly(outputStream);
		}
		return requestBodyContentStr;
	}

	@Override
	public String generateRequestBodyStr(String requestTransType, String requestBodyTemplateStr, boolean needRemoveComments) throws Exception {
		String requestBodyContentStr = (requestBodyTemplateStr != null /* && !requestBodyTemplateStr.trim().equals("") */) ? removeComments(requestBodyTemplateStr) : generateRequestBodyTemplateStr(requestTransType, needRemoveComments);
		Calendar currentDateCalendar = new GregorianCalendar();
		currentDateCalendar.add(Calendar.DATE, dateOffSet);
		String currentDateTimeStr = new SimpleDateFormat(getDateFormatStr(requestTransType)[0]).format(currentDateCalendar.getTime());
		String currentDateStr = new SimpleDateFormat(getDateFormatStr(requestTransType)[1]).format(currentDateCalendar.getTime());
		String currentTimeStr = new SimpleDateFormat(getDateFormatStr(requestTransType)[2]).format(currentDateCalendar.getTime());
		if (requestBodyContentStr.contains("@TRANS_CODE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@TRANS_CODE@", SimulatorProperties.getTransCodePrefix() + new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new GregorianCalendar().getTime())); // 交易流水号，根据实际本地当前时间生成
		if (requestBodyContentStr.contains("@TRANS_DATE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@TRANS_DATE@", currentDateStr); // 交易日期，服务器今日日期
		if (requestBodyContentStr.contains("@APPLY_DATE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@APPLY_DATE@", currentDateStr); // 投保日期，服务器今日日期
		if (requestBodyContentStr.contains("@CHECK_DATE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@CHECK_DATE@", currentDateStr); // 对账日期，服务器今日日期
		if (requestBodyContentStr.contains("@TRANS_TIME@"))
			requestBodyContentStr = requestBodyContentStr.replace("@TRANS_TIME@", currentTimeStr); // 交易时间，本地当前时间
		if (requestBodyContentStr.contains("@TRANS_DATE_TIME@"))
			requestBodyContentStr = requestBodyContentStr.replace("@TRANS_DATE_TIME@", currentDateTimeStr); // 交易日期和时间，服务器今日日期和本地当前时间
		if (requestBodyContentStr.contains("@EFFECT_DATE@") || requestBodyContentStr.contains("@EFFECT_DATE_TIME@")) {
			Calendar effectDateCalendar = new GregorianCalendar();
			effectDateCalendar.setTime(currentDateCalendar.getTime());
			effectDateCalendar.add(Calendar.DATE, 1); // 设置生效日期为投保日期的次日
			if (requestBodyContentStr.contains("@EFFECT_DATE@"))
				requestBodyContentStr = requestBodyContentStr.replace("@EFFECT_DATE@", new SimpleDateFormat(getDateFormatStr(requestTransType)[1]).format(effectDateCalendar.getTime()));
			if (requestBodyContentStr.contains("@EFFECT_DATE_TIME@"))
				requestBodyContentStr = requestBodyContentStr.replace("@EFFECT_DATE_TIME@", new SimpleDateFormat(getDateFormatStr(requestTransType)[0]).format(effectDateCalendar.getTime()));
		}
		if (requestBodyContentStr.contains("@APPLY_CODE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@APPLY_CODE@", CodeUtil.generateCommonCode(SimulatorProperties.getNextApplySerialNum(), "005")); // 银保业务专用投保单
		if (requestBodyContentStr.contains("@POLICY_CODE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@POLICY_CODE@", CodeUtil.generateCommonCode(SimulatorProperties.getNextPolicySerialNum(), "088")); // 个银险保单
		if (requestBodyContentStr.contains("@PRINT_CODE@"))
			requestBodyContentStr = requestBodyContentStr.replace("@PRINT_CODE@", SimulatorProperties.getNextPrintCode()); // 印刷号

		Matcher matcher = Pattern.compile("@((RQ|RP).+?-\\w+)@").matcher(requestBodyContentStr); // 原先的\\w+替换为.+?，因为\w匹配字母或数字或下划线或汉字，而.匹配除换行符以外的任意字符，另外+?为懒惰匹配(重复1次或更多次但尽可能少重复)
		// 正则查找和替换保单号、保费等字段
		while (matcher.find()) {
			String matcherGroup = matcher.group(1);
			if (requestBodyContentStr.contains("@" + matcherGroup + "@")) {
				String elementValue = getElementValue(matcherGroup);
				if (YBTCheckBillUtil.isNeedDiv100(this, requestTransType, matcherGroup) && elementValue != null && !elementValue.trim().equals(""))
					elementValue = elementValue.contains(".") ? String.format("%.2f", Double.parseDouble(elementValue) / 100) : Long.parseLong(elementValue) / 100 + "";
				requestBodyContentStr = requestBodyContentStr.replace("@" + matcherGroup + "@", elementValue);
			}
		}

		// ------------------------------------------------------以下处理日结对账报文-------------------------------------------------------
		int dayCheckSequence = 0;
		// 统计对账Xml的@TOTAL_COUNT@并替换对账保单序号@DAYCHECK_SEQUENCE@，注意一定要是懒惰匹配
		Matcher dayCheckXmlDetailMatcher = Pattern.compile("(<DAYCHECK_DETAIL>[\\w\\W]*?</DAYCHECK_DETAIL>)").matcher(requestBodyContentStr);
		while (dayCheckXmlDetailMatcher.find()) {
			++dayCheckSequence;
			if (requestBodyContentStr.contains("@DAYCHECK_SEQUENCE@")) { // 如果存在序号则逐一替换序号
				String dayCheckXmlDetailContentStr = dayCheckXmlDetailMatcher.group(1); // 获取日结对账Xml内容中单个对账信息节点内容
				requestBodyContentStr = requestBodyContentStr.replaceFirst(dayCheckXmlDetailContentStr, "@DAYCHECK_XML_DETAIL_CONTENT@"); // 获取并替换掉第一个(注意是第一个)dayCheckXmlDetailContentStr
				dayCheckXmlDetailContentStr = dayCheckXmlDetailContentStr.replace("@DAYCHECK_SEQUENCE@", dayCheckSequence + "");
				requestBodyContentStr = requestBodyContentStr.replace("@DAYCHECK_XML_DETAIL_CONTENT@", dayCheckXmlDetailContentStr);
			}
		}
		if (requestBodyContentStr.contains("@TOTAL_COUNT@"))
			requestBodyContentStr = requestBodyContentStr.replace("@TOTAL_COUNT@", dayCheckSequence + "");
		// ------------------------------------------------------日结对账报文处理结束-------------------------------------------------------

		return requestBodyContentStr;
	}

	@Override
	public byte[] generateRequestBodyBytes(String requestTransType, String requestBodyTemplateStr, boolean needRemoveComments) throws Exception {
		String requestBodyContentStr = generateRequestBodyStr(requestTransType, requestBodyTemplateStr, needRemoveComments);
		log.info("交易[" + requestTransType + "]请求报文体: " + requestBodyContentStr);
		transXmlMap.put("RQ" + requestTransType, requestBodyContentStr);
		byte[] requestBodyContentBytes = beforeSend(requestBodyContentStr);
		return requestBodyContentBytes;
	}

	private String removeComments(String requestBodyContentStr) {
		return requestBodyContentStr.replaceAll("<!-- （.*）.* -->", ""); // 去除报文中符合规则的注释，但不包含标准注释
	}

	@Override
	public byte[] generateResponseBodyBytes(String requestTransType) throws Exception {
		return beforeSend("这是 [" + requestTransType + "] 交易由服务器端返回的结果内容。");
	}

	@Override
	public byte[] convertContentStrToBytes(String contentStr) throws Exception {
		return contentStr.getBytes(getClientConfig().getTransEncoding());
	}

	@Override
	public String convertContentBytesToStr(byte[] contentBytes) throws Exception {
		return new String(contentBytes, getClientConfig().getTransEncoding());
	}

	@Override
	public String readHeadConetentStr(String requestTransType, InputStream inputStream) throws IOException {
		String headContentStr = null;
		int readLength = 0;
		byte[] byteHeader = new byte[getHeadLength()];
		while (readLength != byteHeader.length)
			readLength += inputStream.read(byteHeader, readLength, byteHeader.length - readLength);
		headContentStr = new String(byteHeader, getClientConfig().getTransEncoding());
		return headContentStr;
	}

	@Override
	public String readBodyConetentStr(String requestTransType, InputStream inputStream, String headContentStr) throws Exception {
		String bodyContentStr = null;
		int readLength = 0;
		int bodyLength = Integer.parseInt(headContentStr.substring(getBodyLengthBeginIndex(), getBodyLengthEndIndex()).trim());
		byte[] byteBody = new byte[bodyLength];
		while (readLength != bodyLength)
			readLength += inputStream.read(byteBody, readLength, bodyLength - readLength);
		log.debug("交易[" + requestTransType + "]响应未处理的报文体: " + convertContentBytesToStr(byteBody));
		bodyContentStr = convertContentBytesToStr(afterReceived(byteBody));
		transXmlMap.put("RP" + requestTransType, bodyContentStr);
		return bodyContentStr;
	}

	@Override
	public String getServerHost() {
		return serverHost;
	}

	@Override
	public void setServerHost(String serverHost) {
		this.serverHost = serverHost;
	}

	@Override
	public YBTClientConfig getClientConfig() {
		return clientConfig;
	}

	@Override
	public void setClientConfig(YBTClientConfig clientConfig) {
		this.clientConfig = clientConfig;
	}

	@Override
	public void setDateOffSet(int dateOffset) {
		this.dateOffSet = dateOffset;
	}

	@Override
	public void setCurrentDate(String currentDateStr) throws ParseException {
		if (currentDateStr != null && !currentDateStr.trim().equals("")) {
			DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
			Calendar settingCalendar = Calendar.getInstance();
			Calendar currentCalendar = Calendar.getInstance();
			settingCalendar.setTime(dateFormat.parse(currentDateStr));
			currentCalendar.setTime(dateFormat.parse(dateFormat.format(currentCalendar.getTime())));
			dateOffSet = (int) ((settingCalendar.getTimeInMillis() - currentCalendar.getTimeInMillis()) / (1000 * 60 * 60 * 24));
		}
	}

	@Override
	public String getElementValue(String transXmlMapKey) {
		return getElementValue(transXmlMapKey, "");
	}

	@Override
	public String getElementValue(String transXmlMapKey, String defaultValue) {
		return getElementValue(transXmlMap.get(transXmlMapKey.split("-")[0]), transXmlMapKey.split("-")[1], defaultValue);
	}

	private String getElementValue(String xmlString, String elementName, String defaultValue) {
		String elementValue = defaultValue;
		if (xmlString != null && !"".equals(xmlString.trim())) {
			Matcher matcher = Pattern.compile("<" + elementName + ">" + "([^<]*)" + "</" + elementName + ">").matcher(xmlString);
			if (matcher.find())
				elementValue = matcher.group(1);
		}
		return elementValue;
	}

	/** 对报文体做加密、压缩等处理 */
	protected byte[] beforeSend(String bodyContentStr) throws Exception {
		return bodyContentStr.getBytes(getClientConfig().getTransEncoding());
	}

	/** 对报文体做解压缩、解密等处理 */
	protected byte[] afterReceived(byte[] bodyContentBytes) throws Exception {
		return bodyContentBytes;
	}

	/** 返回长度为3的字符串数组，第0个为日期时间，第1一个为日期，第2个为时间 */
	protected String[] getDateFormatStr(String requestTransType) {
		return new String[] { "yyyyMMddHHmmss", "yyyyMMdd", "HHmmss" };
	}

	// protected abstract String getTransEncoding();

	protected abstract int getHeadLength();

	protected abstract int getBodyLengthBeginIndex();

	protected abstract int getBodyLengthEndIndex();
}
